﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FCA.Dto {
    /// <summary>
    /// this is a context in galois
    /// </summary>
    public class Concept {
        #region Properties
        public int ID { get; set; }

        public int Support { get; set; }

        public TidSet Tids { get; set; }

        public ItemSet Items { get; set; }

        public HashSet<int> ParentID { get; set; }

        public HashSet<int> ChildrenID { get; set; }
        #endregion

        #region Constructors
        public Concept() {
            ID = -1;
            Support = 0;
            Tids = new TidSet();
            Items = new ItemSet();
            ChildrenID = new HashSet<int>();
            ParentID = new HashSet<int>();
        }

        public Concept(int id, int sup, HashSet<int> itemSet, HashSet<int> tidSet)
            : this() {
            ID = id;
            Support = sup;
            Items = new ItemSet(itemSet);
            Tids = new TidSet(tidSet);
        }
        #endregion

        public string GetChildrenIDString() {
            return string.Join(",", ChildrenID);
        }

        public string GetParentIDString() {
            return string.Join(",", ParentID);
        }

        public string GetItemsString() {
            return Items.GetValueString();
        }

        public string GetTidsString() {
            return Tids.GetValueString();
        }

        #region get list concept (parents, children)
        public List<Concept> GetChildren(Lattice lat) {
            List<Concept> lst = new List<Concept>();
            foreach (int childID in ChildrenID) {
                if (lat.Concepts.ContainsKey(childID)) {
                    lst.Add(lat.Concepts[childID]);
                }
            }
            return lst;
        }

        public List<Concept> GetParents(Lattice lat) {
            List<Concept> lst = new List<Concept>();
            foreach (int parentID in ParentID) {
                if (lat.Concepts.ContainsKey(parentID)) {
                    lst.Add(lat.Concepts[parentID]);
                }
            }
            return lst;
        }
        #endregion
        #region Add child, parent
        /// <summary>
        /// two way added
        /// </summary>
        /// <param name="c"></param>
        public void AddParent(Concept c) {
            if (ID == c.ID || ParentID.Contains(c.ID)) {
                return;
            }
            ParentID.Add(c.ID);
            c.ChildrenID.Add(ID);
            c.Tids = c.Tids.UnionWith(Tids);
        }

        /// <summary>
        /// one way added
        /// </summary>
        /// <param name="id"></param>
        public void AddParent(int id) {
            if (id == ID || ParentID.Contains(id)) {
                return;
            }
            ParentID.Add(id);
        }

        /// <summary>
        /// two way added
        /// </summary>
        /// <param name="id"></param>
        /// <param name="lat"></param>
        public void AddParent(int id, Lattice lat) {
            if (ID == id || ParentID.Contains(id)) {
                return;
            }
            ParentID.Add(id);
            if (lat.Concepts.ContainsKey(id)) {
                lat.Concepts[id].AddChild(id);
            }
        }


        /// <summary>
        /// two way added
        /// </summary>
        /// <param name="c"></param>
        public void AddChild(Concept c) {
            if (c.ID == ID || ChildrenID.Contains(c.ID)) {
                return;
            }
            ChildrenID.Add(c.ID);
            c.ParentID.Add(ID);
            Tids = Tids.UnionWith(c.Tids);
        }

        /// <summary>
        /// one way added
        /// </summary>
        /// <param name="id"></param>
        public void AddChild(int id) {
            if (id == ID || ChildrenID.Contains(id)) return;
            ChildrenID.Add(id);
        }

        /// <summary>
        /// two way added
        /// </summary>
        /// <param name="id"></param>
        /// <param name="lat"></param>
        public void AddChild(int id, Lattice lat) {
            if (ID == id || ChildrenID.Contains(id)) {
                return;
            }
            ChildrenID.Add(id);
            if (lat.Concepts.ContainsKey(id)) {
                lat.Concepts[id].AddParent(id);
            }
        }
        public void RemoveChild(Concept c) {
            if (ChildrenID.Contains(c.ID)) {
                ChildrenID.Remove(c.ID);
            }
            if (c.ParentID.Contains(ID)) {
                c.ParentID.Remove(ID);
            }
        }
        public void RemoveChild(int id) {
            if (ChildrenID.Contains(id)) {
                ChildrenID.Remove(id);
            }
        }
        public void RemoveParent(Concept c) {
            if (ParentID.Contains(c.ID)) {
                ParentID.Remove(c.ID);
            }
            if (c.ChildrenID.Contains(ID)) {
                c.ChildrenID.Remove(ID);
            }
        }
        public void RemoveParent(int id) {
            if (ParentID.Contains(id)) {
                ParentID.Remove(id);
            }
        }
        #endregion

        #region Add tids, items
        public void AddTid(int tid) {
            Tids.Add(tid);
        }

        public void AddItem(int item) {
            Items.Add(item);
        }
        #endregion
        #region Sort
        /// <summary>
        /// linear-time sorting of the concept set
        /// </summary>
        /// <param name="c"></param>
        /// <param name="m">max items</param>
        public static List<Concept> Sort(List<Concept> c, int m) {
            Dictionary<int, List<Concept>> bunches = new Dictionary<int, List<Concept>>();
            List<Concept> order = new List<Concept>();

            for (int i = 0; i < m; i++) {
                bunches.Add(i, new List<Concept>());
            }

            foreach (Concept context in c) {
                int k = context.Items.Length();
                bunches[k - 1].Add(context);
            }

            foreach (KeyValuePair<int, List<Concept>> pair in bunches) {
                foreach (Concept item in pair.Value) {
                    order.Add(item);
                }
            }
            return order;
        }

        public static void Sort(List<Concept> c) {
            c.Sort(delegate(Concept a, Concept b) {
                return a.Items.Length() - b.Items.Length();
            });
        }
        #endregion
    }
}
